using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using OfficeOpenXml;
using System.Text;
using UnityEditor;
using System;
using SimpleJSON;

public static class Xlsx2Lua
{
    private static readonly string keySeparator = "&";
    private static readonly string ignoreSeparator = "#";

    [MenuItem("Tools/Xlsx2Lua")]
    public static void ExportToLua()
    {
        try
        {
            var folder = Application.dataPath.Replace("Assets", "Tools/Configs");
            var files = Directory.GetFiles(folder, "*.xlsx");

            var output = Application.dataPath + "/Lua/GameLogic/Config";

            var i = 0f;
            EditorUtility.DisplayProgressBar("Export Lua", "开始导出", 0);
            foreach(var file in files)
            {
                try
                {
                    var filename = Path.GetFileNameWithoutExtension(file);
                    if (filename.Contains(@"~$")) continue;

                    EditorUtility.DisplayProgressBar("Export Lua", "开始导出->" + file, i++ / files.Length);
                    var fileInfo = new FileInfo(file);
                    ExcelPackage.LicenseContext = LicenseContext.Commercial;
                    using (var ePkg = new ExcelPackage(fileInfo))
                    {
                        var ws = ePkg.Workbook.Worksheets[0];

                        HashSet<string> rKeys = new HashSet<string>();

                        var nStartColumn = ws.Dimension.Start.Column;
                        var nEndColumn = ws.Dimension.End.Column;
                        var nStartRow = ws.Dimension.Start.Row;
                        var nEndRow = ws.Dimension.End.Row;

                        var keyCol = -1;

                        var opMap = new Dictionary<int, string>();
                        HashSet<int> ingoreColumn = new HashSet<int>();
                        for (int col = nStartColumn; col <= nEndColumn; col++)
                        {
                            var op = ws.GetValue<string>(nStartRow + 2, col);
                            op.Trim();
                            if (keyCol < 0 && op.Contains(keySeparator))
                            {
                                op = op.Replace(keySeparator, string.Empty);
                                keyCol = col;
                            }
                            if (op.Contains(ignoreSeparator))
                            {
                                op = op.Replace(ignoreSeparator, string.Empty);
                                ingoreColumn.Add(col);
                            }
                            opMap.Add(col, op);
                        }

                        StringBuilder sb = new StringBuilder();
                        sb.AppendLine("local t = {");
                        var level = 1;
                        for(int row = nStartRow + 3; row <= nEndRow; row ++)
                        {
                            var tab = new string('\t', level);
                            if (keyCol > 0)
                            {
                                var op = opMap[keyCol];
                                var keyValue = GetItemValue(op, ws, row, keyCol);

                                if (op.Equals("float") || op.Equals("int") || op.Equals("double"))
                                {
                                    keyValue = keyValue.Replace("\"", string.Empty);
                                    keyValue = $"[{keyValue}]";
                                }

                                sb.Append($"{tab}{keyValue} = ");
                                sb.AppendLine("{");
                            }
                            else
                            {
                                sb.Append(tab);
                                sb.AppendLine("{");
                            }

                            for(int col = nStartColumn; col <= nEndColumn; col ++)
                            {
                                if (ingoreColumn.Contains(col)) continue;
                                var keyValue = GetItemValue("string", ws, nStartRow, col);

                                keyValue = keyValue.Replace("\"", string.Empty);

                                var op = opMap[col];
                                var value = GetItemValue(op, ws, row, col, new string('\t', level + 1));
                                sb.AppendLine($"{tab}\t{keyValue} = {value},");
                            }
                            
                            sb.Append(tab);
                            sb.AppendLine("},");
                        }
                        sb.AppendLine("}");
                        sb.Append("return t");

                        if (!Directory.Exists(output))
                            Directory.CreateDirectory(output);

                        var path = string.Format("{0}/{1}.lua", output, filename);
                        File.WriteAllText(path, sb.ToString());
                    }
                }
                catch (System.Exception e)
                {
                    EditorUtility.ClearProgressBar();
                    Debug.LogError($"Failed to Exprot File : {file}\n{e.ToString()}");
                    return;
                }
            }

            EditorUtility.DisplayDialog("导出成功！", $"成功导出Lua配置\n[{output}]", "确定");
            AssetDatabase.Refresh();
            EditorUtility.ClearProgressBar();
        }
        catch (System.Exception e)
        {
            EditorUtility.ClearProgressBar();
            Debug.LogError(e.Message);
        }
    }

    private static string GetItemValue(string op, ExcelWorksheet worksheet, int row, int col, string tab = "")
    {
        string ret = string.Empty;
        switch(op)
        {
            case "string":
                ret = string.Format("\"{0}\"", worksheet.GetValue<string>(row, col));
                break;
            case "int":
                ret = string.Format("{0}", worksheet.GetValue<int>(row, col));
                break;
            case "float":
                ret = string.Format("{0}", worksheet.GetValue<float>(row, col));
                break;
            case "double":
                ret = string.Format("{0}", worksheet.GetValue<double>(row, col));
                break;
            case "bool":
                ret = string.Format("{0}", worksheet.GetValue<bool>(row, col)).ToLower();
                break;
            case "json":
                ret = ConvertJsonToLua(worksheet.GetValue<string>(row, col), tab);
                break;
        }
        return ret;
    }

    // [MenuItem("Tools/Json2Lua")]
    // public static void TestConvertJsonToLua()
    // {
    //     var path = @"Assets\TextMesh Pro\Sprites\EmojiOne.json";
    //     var json = File.ReadAllText(path);
    //     var output = ConvertJsonToLua(json);
    //     output = "local t = " + output;
    //     File.WriteAllText(Application.dataPath + "/Ouput.lua", output);
    //     AssetDatabase.Refresh();
    // }

    private static string ConvertJsonToLua(string json, string tab = "")
    {
        var obj = JSONObject.Parse(json);

        Func<JSONNode, int, string, string> parseAction = null;
        parseAction = (JSONNode node, int level, string tab) => {
            StringBuilder sb = new StringBuilder();
            if (node.IsArray)
            {
                sb.Append("{\n");
                var array = node.AsArray;
                foreach(var item in array)
                {
                    var indent = tab + new string('\t', level + 1);
                    sb.Append(indent);
                    sb.Append(parseAction(item.Value, level + 1, tab));
                    sb.Append(",\n");
                }
                sb.Append(tab + new string('\t', level) + "}");
            }
            else if (node.IsObject)
            {
                sb.Append("{\n");
                var objs = node.AsObject;
                foreach(var item in objs)
                {
                    var indent = tab + new string('\t', level + 1);
                    sb.Append(indent);
                    sb.AppendFormat("{0} = ", item.Key);
                    sb.Append(parseAction(item.Value, level + 1, tab));
                    sb.Append(",\n");
                }
                sb.Append(tab + new string('\t', level) + "}");
            }
            else if (node.IsString)
            {
                sb.Append(string.Format("\"{0}\"", node.Value));
            }
            else if (node.IsBoolean)
            {
                sb.Append(node.Value.ToLower());
            }
            else
            {
                sb.Append(node.Value);
            }
            return sb.ToString();
        };

        
        return parseAction(obj, 0, tab);
    }
}
